<template>
  <view class="lottery">
    <view class="lottery__wrap" :style="{ width: lotterySize + 'rpx', height: lotterySize + 'rpx' }">
      <view class="lottery-action"
        :style="{ width: actionSize + 'rpx', height: actionSize + 'rpx', left: canvasMarginOutside + 'rpx' }" />
      <view class="str-margin-outside" :style="{ left: strMarginOutside + 'rpx' }" />
      <view class="img-margin-str" :style="{ left: imgMarginStr + 'rpx' }" />
      <view class="img-size" :style="{ width: imgWidth + 'rpx', height: imgHeight + 'rpx' }" />
      <template v-if="!!lotteryImg">
        <image class="lottery__bg" mode="widthFix" :src="lotteryBg" :style="{
          width: lotteryPxSize + 'px',
          height: lotteryPxSize + 'px'
        }" />
        <image :class="[
          'lottery__canvas-img',
          { 'lottery__canvas-img-other': !selfRotaty },
          { 'lottery__canvas-img-self': selfRotated }
        ]" mode="widthFix" :src="lotteryImg" :style="{
          width: canvasImgPxSize + 'px',
          height: canvasImgPxSize + 'px',
          left: canvasImgToLeftPx + 'px',
          top: canvasImgToLeftPx + 'px',
          transform: `rotate(${canvasAngle + targetAngle}deg)`,
          transitionDuration: `${transitionDuration}s`
        }" />
        <image class="lottery__action-bg" mode="widthFix" :src="actionBg" :style="{
          width: actionPxSize + 'px',
          height: actionPxSize + 'px',
          left: actionBgToLeftPx + 'px',
          top: actionBgToLeftPx + 'px',
          transform: `rotate(${actionAngle + targetActionAngle}deg)`,
          transitionDuration: `${transitionDuration}s`
        }" @click="handleActionStart" />
      </template>
    </view>
    <!-- 为了兼容 app 端 ctx.measureText 所需的标签 -->
    <text class="lottery__measureText" :style="{ fontSize: higtFontSize + 'px' }">{{ measureText }}</text>
    <!-- #ifdef MP-ALIPAY -->
    <canvas :class="className" :id="canvasId" :width="higtCanvasSize" :height="higtCanvasSize" :style="{
      width: higtCanvasSize + 'px',
      height: higtCanvasSize + 'px'
    }" />
    <!-- #endif -->
    <!-- #ifndef MP-ALIPAY -->
    <canvas :class="className" :canvas-id="canvasId" :width="higtCanvasSize" :height="higtCanvasSize" :style="{
      width: higtCanvasSize + 'px',
      height: higtCanvasSize + 'px'
    }" />
    <!-- #endif -->
  </view>
</template>
<script lang="ts">
  type ResParam = { ok: boolean; data: string; msg: string; };
</script>
<script setup lang="ts">
  import { PropType, ref, computed, nextTick, onMounted, getCurrentInstance } from "vue";
  import { getStore, setStore, clearStore, circleImg, clacTextLen, downloadFile, pathToBase64, base64ToPath } from "./utils";
  import { apiUrl, formatUrl } from "@/util";
  // #region 数据定义
  const
    that = getCurrentInstance(),
    emit = defineEmits<{
      (e: "start", callback: (index: number) => void): void;
      (e: "end"): void;
      (e: "finish", v: ResParam): void;
    }>(),
    props = defineProps({
      // 奖品列表
      list: { type: Array as PropType<gt.sql.prize[]>, required: true },
      // 设计稿的像素比基准值
      pixelRatio: { type: Number, default: 2 },
      // canvas 标识
      canvasId: { type: String, default: "almostLottery" },
      // 渲染延迟
      renderDelay: { type: Number, default: 0 },
      // 抽奖转盘的整体尺寸
      lotterySize: { type: Number, default: 600 },
      // 抽奖按钮的尺寸
      actionSize: { type: Number, default: 200 },
      // canvas边缘距离转盘边缘的距离
      canvasMarginOutside: { type: Number, default: 90 },
      // 奖品区块对应背景颜色
      colors: { type: Array as PropType<string[]>, default: () => ["#FFFFFF", "#FFBF05"] },
      // 字体颜色
      strFontColors: { type: Array as PropType<string[]>, default: () => ["#FFBF05", "#FFFFFF"] },
      // 转盘外环背景图
      lotteryBg: { type: String, default: formatUrl("/static/lottery-bg.png", apiUrl) },
      // 抽奖按钮背景图
      actionBg: { type: String, default: formatUrl("/static/lottery-action.png", apiUrl) },
      // 是否绘制奖品名称
      nameDrawed: { type: Boolean, default: true },
      // 是否开启奖品区块描边
      stroked: { type: Boolean, default: false },
      // 描边颜色
      strokeColor: { type: String, default: "#FFBF05" },
      // 旋转的类型
      rotateType: { type: String, default: "roulette" },
      // 是否开启自转
      selfRotaty: { type: Boolean, default: false },
      // 自转时，最少转多少毫秒
      selfTime: { type: Number, default: 1000 },
      // 旋转动画时间 单位s
      duration: { type: Number, default: 8 },
      // 旋转的圈数
      ringCount: { type: Number, default: 8 },
      // 指针位置
      pointerPosition: { type: String as PropType<"edge" | "middle">, default: "edge" },
      // 文字方向
      strDirection: { type: String as PropType<"horizontal" | "vertical">, default: "horizontal" },
      // 文字的大小
      strFontSize: { type: Number, default: 24 },
      // 奖品文字距离边缘的距离
      strMarginOutside: { type: Number, default: 0 },
      // 奖品图片距离奖品文字的距离
      imgMarginStr: { type: Number, default: 60 },
      // 奖品文字多行情况下的行高
      strLineHeight: { type: Number, default: 1.2 },
      // 奖品文字总长度限制
      strMaxLen: { type: Number, default: 12 },
      // 奖品文字多行情况下第一行文字长度
      strLineLen: { type: Number, default: 6 },
      // 奖品图片的宽
      imgWidth: { type: Number, default: 50 },
      // 奖品图片的高
      imgHeight: { type: Number, default: 50 },
      // 是否绘制奖品图片
      imgDrawed: { type: Boolean, default: true },
      // 奖品图片是否裁切为圆形
      imgCircled: { type: Boolean, default: false },
      // 转盘绘制成功的提示
      successMsg: { type: String, default: "奖品准备就绪，快来参与抽奖吧" },
      // 转盘绘制失败的提示
      failMsg: { type: String, default: "奖品仍在准备中，请稍后再来..." },
      // 是否开启画板的缓存
      canvasCached: { type: Boolean, default: false },
    }),
    // 画板className
    className = ref("lottery__canvas"),
    // 高清固定 2 倍，不再从 system 中动态获取，因为 h5、app-vue 中单个尺寸过大时存在 iOS/Safari 无法绘制的问题，且 2 倍基本也可以解决模糊的问题
    systemPixelRatio = ref(2),
    // 抽奖转盘的整体px尺寸
    lotteryPxSize = ref(0),
    // 画板的px尺寸
    canvasImgPxSize = ref(0),
    // 抽奖按钮的px尺寸
    actionPxSize = ref(0),
    // 奖品文字距离转盘边缘的距离
    strMarginPxOutside = ref(0),
    // 奖品图片相对奖品文字的距离
    imgMarginPxStr = ref(0),
    // 奖品图片的宽、高
    imgPxWidth = ref(0),
    imgPxHeight = ref(0),
    // 画板导出的图片
    lotteryImg = ref(""),
    // 旋转到奖品目标需要的角度
    targetAngle = ref(0),
    targetActionAngle = ref(0),
    // 配合自转使用
    selfRotated = ref(false),
    selfRotatyStartTime = ref(0),
    // 是否正在旋转
    isRotate = ref(false),
    // 当前停留在那个奖品的序号
    stayIndex = ref(0),
    // 当前中奖奖品的序号
    targetIndex = ref(0),
    // 是否存在可用的缓存转盘图
    isCacheImg = ref(false),
    oldLotteryImg = ref(""),
    // 解决 app 不支持 measureText 的问题
    // app 已在 2.9.3 的版本中提供了对 measureText 的支持，将在后续版本逐渐稳定后移除相关兼容代码
    measureText = ref(""),
    // 高清尺寸
    higtCanvasSize = computed(() => {
      return canvasImgPxSize.value * systemPixelRatio.value;
    }),
    // 高清字体
    higtFontSize = computed(() => Math.round(props.strFontSize / props.pixelRatio) * systemPixelRatio.value),
    // 高清行高
    higtHeightMultiple = computed(() => Math.round(props.strFontSize / props.pixelRatio) * props.strLineHeight * systemPixelRatio.value),
    canvasImgToLeftPx = computed(() => (lotteryPxSize.value - canvasImgPxSize.value) / 2),
    actionBgToLeftPx = computed(() => (lotteryPxSize.value - actionPxSize.value) / 2),
    // 根据奖品列表计算 canvas 旋转角度
    canvasAngle = computed(() => {
      let result = 0;
      let count = props.list.length;
      let clip = 360 / count;
      let diffNum = 90 / clip;
      if (props.pointerPosition === "edge" || props.rotateType === "pointer") {
        result = -(clip * diffNum);
      } else {
        result = -(clip * diffNum + clip / 2);
      }
      return result;
    }),
    actionAngle = computed(() => 0),
    // 外圆的半径
    outsideRadius = computed(() => higtCanvasSize.value / 2),
    // 内圆的半径
    insideRadius = computed(() => 20 * systemPixelRatio.value),
    // 文字距离边缘的距离
    textRadius = computed(() => strMarginPxOutside.value * systemPixelRatio.value || (higtFontSize.value / 2)),
    // 根据画板的宽度计算奖品文字与中心点的距离
    textDistance = computed(() => {
      const textZeroY = Math.round(outsideRadius.value - (insideRadius.value / 2));
      return textZeroY - textRadius.value;
    }),
    // 旋转动画时间 单位 s
    transitionDuration = computed(() => props.selfRotaty ? 2 : props.duration)
    ;
  // #endregion 数据定义结束
  // 开始旋转
  function onRotateStart() {
    // 奖品总数
    if (!props.selfRotaty) {
      if (isRotate.value) return;
      isRotate.value = true;
    }
    let count = props.list.length;
    let baseAngle = 360 / count;
    let angles = 0;
    let ringCount = props.selfRotaty ? 1 : props.ringCount;
    if (props.rotateType === "pointer") {
      if (targetActionAngle.value === 0) {
        // 第一次旋转
        angles = (targetIndex.value - stayIndex.value) * baseAngle + baseAngle / 2 - actionAngle.value;
      } else {
        // 后续旋转
        // 后续继续旋转 就只需要计算停留的位置与目标位置的角度
        angles = (targetIndex.value - stayIndex.value) * baseAngle;
      }
      // 更新目前序号
      stayIndex.value = targetIndex.value;
      // 转 8 圈，圈数越多，转的越快
      targetActionAngle.value += angles + 360 * ringCount;
    } else {
      if (targetAngle.value === 0) {
        // 第一次旋转
        // 因为第一个奖品是从0°开始的，即水平向右方向
        // 第一次旋转角度 = 270度 - (停留的序号-目标序号) * 每个奖品区间角度 - 每个奖品区间角度的一半 - canvas自身旋转的度数
        angles = (270 - (targetIndex.value - stayIndex.value) * baseAngle - baseAngle / 2) - canvasAngle.value;
      } else {
        // 后续旋转
        // 后续继续旋转 就只需要计算停留的位置与目标位置的角度
        angles = -(targetIndex.value - stayIndex.value) * baseAngle;
      }
      // 更新目前序号
      stayIndex.value = targetIndex.value;
      // 转 8 圈，圈数越多，转的越快
      targetAngle.value += angles + 360 * ringCount;
    }
    // 计算转盘结束的时间，预加一些延迟确保转盘停止后触发结束事件
    setTimeout(() => {
      isRotate.value = false;
      emit("end");
    }, props.selfRotaty ? 0 : (transitionDuration.value * 1000 + 100));
  }
  // 点击 开始抽奖 按钮
  function handleActionStart() {
    if (!lotteryImg.value || isRotate.value) return;
    emit("start", (index: number) => {
      if (!(index >= 0 && index < props.list.length)) return;
      if (props.selfRotaty) {
        isRotate.value = true;
        selfRotated.value = true;
        selfRotatyStartTime.value = Date.now();
        const diffTime = Date.now() - selfRotatyStartTime.value;
        const timeDelay = diffTime < props.selfTime ? props.selfTime : 0;
        setTimeout(() => {
          selfRotated.value = false;
          targetIndex.value = index;
          onRotateStart();
        }, timeDelay);
      } else {
        targetIndex.value = index;
        onRotateStart();
      }
    });
  }
  // 渲染转盘
  async function onCreateCanvas() {
    // 获取 canvas 画布
    const canvasId = props.canvasId;
    const ctx = uni.createCanvasContext(canvasId, that);
    // canvas 的宽高
    let canvasW = higtCanvasSize.value;
    let canvasH = higtCanvasSize.value;
    // 根据奖品个数计算 角度
    let count = props.list.length;
    let baseAngle = Math.PI * 2 / count;
    // 设置字体
    ctx.setFontSize(higtFontSize.value);
    // 注意，开始画的位置是从0°角的位置开始画的。也就是水平向右的方向。
    // 画具体内容
    for (let i = 0; i < count; i++) {
      let item = props.list[i];
      // 当前角度
      let angle = i * baseAngle;
      // 保存当前画布的状态
      ctx.save();
      // x => 圆弧对应的圆心横坐标 x
      // y => 圆弧对应的圆心横坐标 y
      // radius => 圆弧的半径大小
      // startAngle => 圆弧开始的角度，单位是弧度
      // endAngle => 圆弧结束的角度，单位是弧度
      // anticlockwise(可选) => 绘制方向，true 为逆时针，false 为顺时针
      ctx.beginPath();
      // 外圆
      ctx.arc(canvasW * 0.5, canvasH * 0.5, outsideRadius.value, angle, angle + baseAngle, false);
      // 内圆
      ctx.arc(canvasW * 0.5, canvasH * 0.5, insideRadius.value, angle + baseAngle, angle, true);
      // 每个奖品区块背景填充颜色
      ctx.setFillStyle(props.colors[i % props.colors.length]);
      // 填充颜色
      ctx.fill();
      // 开启描边
      if (props.stroked) {
        // 设置描边颜色
        ctx.setStrokeStyle(`${props.strokeColor}`);
        // 描边
        ctx.stroke();
      }
      // 开始绘制奖品内容
      // 重新映射画布上的 (0,0) 位置
      let translateX = canvasW * 0.5 + Math.cos(angle + baseAngle / 2) * textDistance.value;
      let translateY = canvasH * 0.5 + Math.sin(angle + baseAngle / 2) * textDistance.value;
      ctx.translate(translateX, translateY);
      // 绘制奖品名称
      let rewardName = strLimit(item.name);
      // 设置文字颜色
      ctx.setFillStyle(props.strFontColors[i % props.strFontColors.length]);
      // rotate方法旋转当前的绘图，因为文字是和当前扇形中心线垂直的
      ctx.rotate(angle + (baseAngle / 2) + (Math.PI / 2));
      // 设置文本位置并处理换行
      if (props.strDirection === "horizontal") {
        // 是否需要换行
        if (rewardName && props.nameDrawed) {
          let realLen = clacTextLen(rewardName).realLen;
          let isLineBreak = realLen > props.strLineLen;
          if (isLineBreak) {
            // 获得多行文本数组
            let textCount = 0;
            let tempTxt = "";
            let rewardNames = [];
            for (let j = 0; j < rewardName.length; j++) {
              textCount += clacTextLen(rewardName[j]).byteLen;
              tempTxt += rewardName[j];
              if (textCount >= (props.strLineLen * 2)) {
                rewardNames.push(tempTxt);
                textCount = 0;
                tempTxt = "";
              } else {
                if ((rewardName.length - 1) === j) {
                  rewardNames.push(tempTxt);
                  textCount = 0;
                  tempTxt = "";
                }
              }
            }
            // 循环文本数组，计算每一行的文本宽度
            for (let j = 0; j < rewardNames.length; j++) {
              if (ctx.measureText && ctx.measureText(rewardNames[j]).width > 0) {
                // 文本的宽度信息
                let tempStrSize = ctx.measureText(rewardNames[j]);
                let tempStrWidth = -(tempStrSize.width / 2).toFixed(2);
                ctx.fillText(rewardNames[j], tempStrWidth, j * higtHeightMultiple.value);
              } else {
                measureText.value = rewardNames[j];
                // 等待页面重新渲染
                await nextTick();
                let textWidth = await getTextWidth();
                let tempStrWidth = -(textWidth / 2).toFixed(2);
                ctx.fillText(rewardNames[j], tempStrWidth, j * higtHeightMultiple.value);
              }
            }
          } else {
            if (ctx.measureText && ctx.measureText(rewardName).width > 0) {
              // 文本的宽度信息
              let tempStrSize = ctx.measureText(rewardName);
              let tempStrWidth = -(tempStrSize.width / 2).toFixed(2);
              ctx.fillText(rewardName, tempStrWidth, 0);
            } else {
              measureText.value = rewardName;
              // 等待页面重新渲染
              await nextTick();
              let textWidth = await getTextWidth();
              let tempStrWidth = -(textWidth / 2).toFixed(2);
              ctx.fillText(rewardName, tempStrWidth, 0);
            }
          }
        }
      } else {
        let rewardNames = rewardName.split("");
        for (let j = 0; j < rewardNames.length; j++) {
          if (ctx.measureText && ctx.measureText(rewardNames[j]).width > 0) {
            // 文本的宽度信息
            let tempStrSize = ctx.measureText(rewardNames[j]);
            let tempStrWidth = -(tempStrSize.width / 2).toFixed(2);
            ctx.fillText(rewardNames[j], tempStrWidth, j * higtHeightMultiple.value);
          } else {
            measureText.value = rewardNames[j];
            // 等待页面重新渲染
            await nextTick();
            let textWidth = await getTextWidth();
            let tempStrWidth = -(textWidth / 2).toFixed(2);
            ctx.fillText(rewardNames[j], tempStrWidth, j * higtHeightMultiple.value);
          }
        }
      }
      // 绘制奖品图片，文字竖向展示时，不支持图片展示
      if (props.imgDrawed && item.image && props.strDirection !== "vertical") {
        // App-Android平台 系统 webview 更新到 Chrome84+ 后 canvas 组件绘制本地图像 uni.canvasToTempFilePath 会报错
        // 统一将图片处理成 base64
        // https://ask.dcloud.net.cn/question/103303
        let reg = /^(https|http)/g;
        // 处理远程图片
        if (reg.test(item.image)) {
          const res = await downloadFile(item.image);
          if (res.ok) {
            const tempFilePath = res.tempFilePath;
            // #ifndef MP
            item.image = await pathToBase64(tempFilePath);
            // #endif
            // #ifdef MP
            item.image = tempFilePath;
            // #endif
          } else {
            handleimgSuc(res);
          }
        } else {
          // #ifndef MP
          // 不是小程序环境，把本地图片处理成 base64
          if (item.image.indexOf(";base64,") === -1) {
            item.image = await pathToBase64(item.image);
          }
          // #endif
          // #ifdef MP-WEIXIN
          // 小程序环境，把 base64 处理成小程序的本地临时路径
          if (item.image.indexOf(";base64,") !== -1) {
            item.image = await base64ToPath(item.image);
          }
          // #endif
        }
        let imageX = -(imgPxWidth.value * systemPixelRatio.value / 2);
        let imageY = imgMarginPxStr.value * systemPixelRatio.value;
        let imageW = imgPxWidth.value * systemPixelRatio.value;
        let imageH = imgPxHeight.value * systemPixelRatio.value;
        if (props.imgCircled) {
          // 重新设置每个圆形的背景色
          ctx.setFillStyle(props.colors[i % props.colors.length]);
          circleImg(ctx, item.image, imageX, imageY, imageW, imageH);
        } else {
          ctx.drawImage(item.image, imageX, imageY, imageW, imageH);
        }
      }
      ctx.restore();
    }
    // 保存绘图并导出图片
    ctx.draw(true, () => {
      setTimeout(() => {
        // #region #ifdef MP-ALIPAY
        // #ifdef MP-ALIPAY
        // @ts-ignore
        ctx.toTempFilePath({
          destWidth: higtCanvasSize.value,
          destHeight: higtCanvasSize.value,
          success: (res: any) => {
            handleimg({
              ok: true,
              data: res.apFilePath,
              msg: "画布导出生成图片成功"
            });
          },
          fail: (err: any) => {
            handleimg({
              ok: false,
              data: err,
              msg: "画布导出生成图片失败"
            });
          }
        });
        // #endif
        // #endregion
        // #ifndef MP-ALIPAY
        uni.canvasToTempFilePath({
          canvasId: props.canvasId,
          destWidth: higtCanvasSize.value,
          destHeight: higtCanvasSize.value,
          success: (res) => {
            // 在 H5 平台下，tempFilePath 为 base64
            handleimg({
              ok: true,
              data: res.tempFilePath,
              msg: "画布导出生成图片成功"
            });
          },
          fail: (err) => {
            handleimg({
              ok: false,
              data: err,
              msg: "画布导出生成图片失败"
            });
          }
        }, that);
        // #endif
      }, 500);
    });
  }
  // 处理导出的图片
  function handleimg(res: ResParam) {
    if (res.ok) {
      let data = res.data;
      if (!props.canvasCached) {
        lotteryImg.value = data;
        handleimgSuc(res);
        return;
      }
      // #ifndef H5
      if (isCacheImg.value) {
        uni.getSavedFileList({
          success: (sucRes) => {
            const fileList = sucRes.fileList;
            let cached = false;
            if (fileList.length) {
              for (let i = 0; i < fileList.length; i++) {
                let item = fileList[i];
                if (item.filePath === data) {
                  cached = true;
                  lotteryImg.value = data;
                  handleimgSuc(res);
                  break;
                }
              }
            }
            if (!cached) {
              initCanvasDraw();
            }
          },
          fail() {
            initCanvasDraw();
          }
        });
      } else {
        uni.saveFile({
          tempFilePath: data,
          success: (sucRes) => {
            let filePath = sucRes.savedFilePath;
            setStore(`${props.canvasId}LotteryImg`, filePath);
            lotteryImg.value = filePath;
            handleimgSuc({
              ok: true,
              data: filePath,
              msg: "画布导出生成图片成功"
            });
          },
          fail: (err) => {
            handleimg({
              ok: false,
              data: err,
              msg: "画布导出生成图片失败"
            });
          }
        });
      }
      // #endif
      // #ifdef H5
      setStore(`${props.canvasId}LotteryImg`, data);
      lotteryImg.value = data;
      handleimgSuc(res);
      // #endif
    }
  }
  // 处理图片完成
  function handleimgSuc(res: ResParam) {
    emit("finish", {
      ok: res.ok,
      data: res.data,
      msg: res.ok ? props.successMsg : props.failMsg
    });
  }
  // 兼容 app 端不支持 ctx.measureText 已知问题：初始绘制时，低端安卓机 平均耗时 2s hbx 2.8.12+ 已在 app 端支持
  function getTextWidth() {
    let query = uni.createSelectorQuery().in(that);
    let nodesRef = query.select(".lottery__measureText");
    return new Promise<number>((resolve) => {
      nodesRef.fields({
        size: true,
      }, (res) => {
        resolve((Array.isArray(res) ? res[0].width : res.width) || 0);
      }).exec();
    });
  }
  // 处理文字溢出
  function strLimit(value: string) {
    let maxLength = props.strMaxLen;
    if (!value || !maxLength) return value;
    return clacTextLen(value).realLen > maxLength ? value.slice(0, maxLength - 1) + ".." : value;
  }
  // 检查本地缓存中是否存在转盘图
  function checkCacheImg() {
    // 检查是否已有缓存的转盘图
    // 检查是否与本次奖品数据相同
    oldLotteryImg.value = getStore(`${props.canvasId}LotteryImg`);
    let oldlist = getStore(`${props.canvasId}list`);
    let newlist = JSON.stringify(props.list);
    if (oldLotteryImg.value) {
      if (oldlist === newlist) {
        isCacheImg.value = true;
        handleimg({
          ok: true,
          data: oldLotteryImg.value,
          msg: "画布导出生成图片成功"
        });
        return;
      }
    }
    initCanvasDraw();
  }
  // 初始化绘制
  function initCanvasDraw() {
    isCacheImg.value = false;
    lotteryImg.value = "";
    clearStore(`${props.canvasId}LotteryImg`);
    setStore(`${props.canvasId}list`, props.list);
    onCreateCanvas();
  }
  // 预处理初始化
  async function beforeInit() {
    let query = uni.createSelectorQuery().in(that);
    // 处理 rpx 自适应尺寸
    let lotterySize = await new Promise<Required<UniApp.NodeInfo>>((resolve) => {
      query.select(".lottery__wrap").boundingClientRect((rects) => {
        resolve(Array.isArray(rects) ? rects[0] : rects as any);
      }).exec();
    });
    let actionSize = await new Promise<Required<UniApp.NodeInfo>>((resolve) => {
      query.select(".lottery-action").boundingClientRect((rects) => {
        resolve(Array.isArray(rects) ? rects[0] : rects as any);
      }).exec();
    });
    let strMarginSize = await new Promise<Required<UniApp.NodeInfo>>((resolve) => {
      query.select(".str-margin-outside").boundingClientRect((rects) => {
        resolve(Array.isArray(rects) ? rects[0] : rects as any);
      }).exec();
    });
    let imgMarginStr = await new Promise<Required<UniApp.NodeInfo>>((resolve) => {
      query.select(".img-margin-str").boundingClientRect((rects) => {
        resolve(Array.isArray(rects) ? rects[0] : rects as any);
      }).exec();
    });
    let imgSize = await new Promise<Required<UniApp.NodeInfo>>((resolve) => {
      query.select(".img-size").boundingClientRect((rects) => {
        resolve(Array.isArray(rects) ? rects[0] : rects as any);
      }).exec();
    });
    lotteryPxSize.value = Math.floor(lotterySize.width);
    canvasImgPxSize.value = lotteryPxSize.value - Math.floor(actionSize.left) + Math.floor(lotterySize.left);
    actionPxSize.value = Math.floor(actionSize.width);
    strMarginPxOutside.value = Math.floor(strMarginSize.left) - Math.floor(lotterySize.left);
    imgMarginPxStr.value = Math.floor(imgMarginStr.left) - Math.floor(lotterySize.left);
    imgPxWidth.value = Math.floor(imgSize.width);
    imgPxHeight.value = Math.floor(imgSize.height);
    const stoTimer = setTimeout(() => {
      clearTimeout(stoTimer);
      // 判断画板是否设置缓存
      if (props.canvasCached) {
        checkCacheImg();
      } else {
        initCanvasDraw();
      }
    }, 50);
  }
  onMounted(() => {
    nextTick(() => {
      const stoTimer = setTimeout(() => {
        clearTimeout(stoTimer);
        beforeInit();
      }, 50 + props.renderDelay);
    });
  });
</script>
<style lang="scss" scoped>
  .lottery {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
  }
  // 以下元素不可见，是 canvas 的实例
  .lottery__canvas {
    position: absolute;
    left: -9999px;
    opacity: 0;
    display: flex;
    justify-content: center;
    align-items: center;
  }
  // 以下元素不可见，用于获得自适应的值
  .lottery-action,
  .str-margin-outside,
  .img-margin-str,
  .img-size {
    position: absolute;
    left: 0;
    top: 0;
    z-index: -1;
    // background-color: blue;
  }
  // 以下元素不可见，用于计算文本的宽度
  .lottery__measureText {
    position: absolute;
    left: 0;
    top: 0;
    white-space: nowrap;
    font-size: 12px;
    opacity: 0;
  }
  // 以下为可见内容的样式
  .lottery__wrap {
    position: relative;
    // display: flex;
    // justify-content: center;
    // align-items: center;
    // background-color: #FFFFFF;
  }
  .lottery__bg,
  .lottery__canvas-img,
  .lottery__action-bg {
    position: absolute;
    left: 0;
    top: 0;
  }
  .lottery__canvas-img-other {
    transition: transform cubic-bezier(.34, .12, .05, .95);
  }
  @keyframes selfRotate {
    0% {
      transform: rotate(0deg);
    }
    50% {
      transform: rotate(180deg);
    }
    100% {
      transform: rotate(360deg);
    }
  }
  .lottery__canvas-img-self {
    transition: transform ease-in;
    animation: selfRotate .6s linear infinite;
  }
</style>
